﻿using AutoMapper;
using MediatR;
using Microsoft.EntityFrameworkCore;
using WeatherHistory.Data.Entities;
using WeatherHistory.Data.Repositories.Interfaces;
using WeatherHistory.Shared.Exceptions;

namespace WeatherHistory.Api.CommandsQueries.Commands.Regions.AttachWeather;

public class AttachWeatherRegionsCommandHandler : IRequestHandler<AttachWeatherRegionsCommandRequest, AttachWeatherRegionsCommandResponse>
{
    private readonly IMapper _mapper;
    private readonly IRegionRepository _regionRepository;
    private readonly IWeatherRegionRelationRepository _weatherRegionRelationRepository;
    private readonly IWeatherRepository _weatherRepository;

    public AttachWeatherRegionsCommandHandler(IMapper mapper, IWeatherRepository weatherRepository, IRegionRepository regionRepository, IWeatherRegionRelationRepository weatherRegionRelationRepository)
    {
        _mapper = mapper;
        _weatherRepository = weatherRepository;
        _regionRepository = regionRepository;
        _weatherRegionRelationRepository = weatherRegionRelationRepository;
    }

    public async Task<AttachWeatherRegionsCommandResponse> Handle(AttachWeatherRegionsCommandRequest request, CancellationToken cancellationToken)
    {
        var region = await _regionRepository
            .AsQueryable()
            .Include(x => x.Forecasts)
            .FirstOrDefaultAsync(x => x.Id == request.RegionId, cancellationToken);
        if (region == null) throw new NotFoundException($"Регион с id '{request.RegionId}' не найден");

        var weather = await _weatherRepository
            .AsQueryable()
            .Include(x => x.Forecasts)
            .FirstOrDefaultAsync(x => x.Id == request.WeatherId, cancellationToken);
        if (weather == null) throw new NotFoundException($"Погода с id '{request.WeatherId}' не найдена");

        var weatherForecasts = weather.Forecasts.Select(x => x.Id);
        var regionForecasts = region.Forecasts.Select(x => x.Id);
        var onlyWeatherForecasts = weatherForecasts.Except(regionForecasts).ToList();
        if (onlyWeatherForecasts.Any()) throw new NotFoundException($"Прогнозы погоды с id '{string.Join("', '", onlyWeatherForecasts)}' есть в записи погоды с id '{request.WeatherId}', но нет в регионе с id '{request.RegionId}'");

        var storedWeatherRelation = await _weatherRegionRelationRepository
            .AsQueryable()
            .Include(x => x.Region)
            .Include(x => x.Weather).ThenInclude(x => x.Forecasts)
            .FirstOrDefaultAsync(x => x.RegionId == request.RegionId && x.WeatherId == request.WeatherId, cancellationToken);

        if (storedWeatherRelation != null)
        {
            storedWeatherRelation.CreatedAt = DateTime.UtcNow;
            await _weatherRegionRelationRepository.UpdateAsync(storedWeatherRelation, cancellationToken);
        }
        else
        {
            storedWeatherRelation = new WeatherRegionRelation();
            storedWeatherRelation.Region = region;
            storedWeatherRelation.Weather = weather;
            await _weatherRegionRelationRepository.InsertAsync(storedWeatherRelation, cancellationToken);
        }

        return _mapper.Map<AttachWeatherRegionsCommandResponse>(storedWeatherRelation);
    }
}